package main;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import metamodels.Metamodel;
import metamodels.MetamodelParser;
import models.Model;
import models.ModelParser;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;

import utils.Utils;
import errors.Error;

public class Main {

	private static String metamodel = "";
	private static String model = "";
	private static boolean parse = false;
	private static boolean verbose = false;
	private static boolean k = false;

	public static void main(String[] args) {

		CommandLineParser cmdparser = new PosixParser();
		try {
			CommandLine cmd = cmdparser.parse(getOptions(), args);

			loadOptions(cmd, getOptions());
			execute();
		} catch (ParseException e) {
			Error.report(e.getMessage());
		}
	}

	private static void execute() {

		// parse
		if (!metamodel.equals("") && !model.equals("") && parse) {
			parse(metamodel, model, verbose);
		}

		// parse metamodel
		else if (!metamodel.equals("") && parse) {
			parse_mm(metamodel, verbose);
		}

		// parse model
		else if (!model.equals("") && parse) {
			parse_m(model, verbose);
		}

		// generate K
		else if (!metamodel.equals("") && !model.equals("") && k) {
			k(metamodel, model, verbose);
		}

	}

	private static void k(String metamodel, String model, boolean verbose2) {

		MetamodelParser mmparser = new MetamodelParser();
		Metamodel mm = mmparser.parse(metamodel, Utils.MM_CGT, verbose);
		// System.out.println(mm.toK());

		ModelParser mparser = new ModelParser();
		Model m = mparser.parse(model, Utils.M_CGT, verbose);

		String mmkfile = new File(new File(metamodel).getAbsolutePath()).getParent() + Utils.SEP
				+ "classdiagram.k";
		saveInFile(mmkfile, mm.toK());

		String mkfile = new File(new File(model).getAbsolutePath()).getParent() + Utils.SEP + "model.k";
		saveInFile(mkfile, m.toK(mm));

//		ProcessBuilder pb = new ProcessBuilder(Utils.COMPILE, Utils.KOMPILE,
//				Utils.K_PRELUDE, Utils.VALUES, mkfile);
//		try {
//			pb.start();
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//
//		System.out.println("Finished!");
	}

	private static void parse(String metamodel, String model, boolean verbose) {

		MetamodelParser mmparser = new MetamodelParser();
		mmparser.parse(metamodel, Utils.MM_CGT, verbose);
		// System.out.println(mm.toK());

		ModelParser mparser = new ModelParser();
		mparser.parse(model, Utils.M_CGT, verbose);
		// System.out.println(m.toK(mm));
	}

	private static void parse_mm(String metamodel, boolean verbose2) {
		MetamodelParser mmparser = new MetamodelParser();
		mmparser.parse(metamodel, Utils.MM_CGT, verbose);
	}

	private static void parse_m(String model, boolean verbose2) {
		ModelParser mparser = new ModelParser();
		mparser.parse(model, Utils.M_CGT, verbose);
	}

	public static Options getOptions() {
		Options options = new Options();

		Option help = new Option("help", false, "prints this message");
		Option metamodel = new Option("metamodel", true, "metamodel file");
		Option model = new Option("model", true, "model file");
		Option parse = new Option("parse", false, "parse only");
		Option k = new Option("k", false, "generate k");
		Option verbose = new Option("verbose", false, "verbose mode");

		options.addOption(help);
		options.addOption(metamodel);
		options.addOption(model);
		options.addOption(parse);
		options.addOption(k);
		options.addOption(verbose);

		return options;
	}

	private static void loadOptions(CommandLine cmd, Options options) {

		if (cmd.hasOption("help")) {
			HelpFormatter formatter = new HelpFormatter();
			formatter.printHelp("kml --metamodel `metamodel.mm` --model `model.m` [--parse] [--k] [--verbose]\n\n", options);
			System.exit(1);
		}

		if (cmd.hasOption("model")) {
			model = cmd.getOptionValue("model");
		}

		if (cmd.hasOption("metamodel")) {
//			Error.report("No metamodel given as input.\nUse `--help` option for more information.");
//		} else {
			metamodel = cmd.getOptionValue("metamodel");
		}

		if (cmd.hasOption("parse")) {
			parse = true;
		}

		if (cmd.hasOption("verbose")) {
			verbose = true;
		}

		if (cmd.hasOption("k")) {
			if (!(cmd.hasOption("metamodel") && cmd.hasOption("model"))) {
				Error.report("You have to provide both model and metamodel when -k option is used.\n");
			}

			k = true;
		}

	}

	private static void saveInFile(String mmkfile, String content) {
		try {
			File file1 = new File(mmkfile);
			File f2 = new File(file1.getParent());
			f2.mkdirs();
			// file1.createNewFile();

			BufferedWriter out = new BufferedWriter(new FileWriter(file1));
			if (content != null) {
				out.write(content);
				out.flush();
				out.close();
			}
		} catch (IOException e) {
			Error.report("Internal error: Cannot save file content.");
		}
	}

}
